﻿// HSS.Security.Cryptography.Win32Crypto.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       Win32Crypto.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/04/2010
// ----------------------------------------------------------------------------
namespace HSS.Security.Cryptography
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using System.Runtime.InteropServices;
	using System.Text;
	#endregion

	#region Win32 Crypto
	internal class Win32Crypto
	{
		public static string[] EnumerateKeyContainers(bool useMachineStore, bool silent)
		{
			// NOTE: http://msdn.microsoft.com/en-us/library/aa379886.aspx
			//		When dwFlags is set to CRYPT_VERIFYCONTEXT, pszContainer must be set to NULL.


			IntPtr h;
			if (!CryptAcquireContext(out h, null, null, PROV_RSA_FULL, Win32Crypto.GetProviderFlags(true, useMachineStore, silent)))
				throw createWin32Exception("CryptAcquireContext");
			try
			{
				List<string> list = new List<string>();
				uint flags = CRYPT_FIRST;
				uint cch = 1024; // arbitrary, but reasonable max length
				StringBuilder containerName = new StringBuilder((int)cch, (int)cch);
				while (CryptGetProvParamAnsiString(h, PP_ENUMCONTAINERS, containerName, ref cch, flags))
				{
					list.Add(containerName.ToString());
					flags = CRYPT_NEXT;
				}
				int lastWin32Error = Marshal.GetLastWin32Error();
				if (ERROR_NO_MORE_ITEMS != lastWin32Error)
				{
					throw createWin32Exception("CryptGetProvParam", lastWin32Error);
				}
				return list.ToArray();
			}
			finally
			{
				if (!CryptReleaseContext(h, 0))
				{
					throw createWin32Exception("CryptReleaseContext");
				}
			}
		}
		public static void DeleteKeyContainer(string keyContainerName, bool useMachineStore, bool silent)
		{
			// NOTE: http://msdn.microsoft.com/en-us/library/aa379886.aspx
			//		When dwFlags is set to CRYPT_VERIFYCONTEXT, pszContainer must be set to NULL.


			IntPtr h;
			if (!CryptAcquireContext(out h, keyContainerName, null, PROV_RSA_FULL,
				Win32Crypto.GetProviderFlags(false, useMachineStore, silent) | CRYPT_DELETEKEYSET))
				throw createWin32Exception("CryptAcquireContext");
		}
		public static bool KeyContainerExists(string keyContainerName, bool useMachineStore, bool silent)
		{
			// NOTE: http://msdn.microsoft.com/en-us/library/aa379886.aspx
			//		When dwFlags is set to CRYPT_VERIFYCONTEXT, pszContainer must be set to NULL.


			IntPtr h;
			bool success = CryptAcquireContext(out h, keyContainerName, null, PROV_RSA_FULL, Win32Crypto.GetProviderFlags(false, useMachineStore, silent));
			int win32Err = Marshal.GetLastWin32Error();
			if (success)
			{
				if (!CryptReleaseContext(h, 0))
					throw createWin32Exception("CryptReleaseContext");
				return true;
			}
			else
			{
				if (NTE_BAD_KEYSET == win32Err)
					return false;
				throw createWin32Exception("CryptAcquireContext", win32Err);
			}
		}

		static uint GetProviderFlags(bool verify, bool machineStore, bool silent)
		{
			// NOTE: http://msdn.microsoft.com/en-us/library/aa379886.aspx
			//		When dwFlags is set to CRYPT_VERIFYCONTEXT, pszContainer must be set to NULL.

			uint flags = 0;
			if (verify)
				flags = flags | CRYPT_VERIFYCONTEXT;
			if (silent)
				flags = flags | CRYPT_SILENT;
			if (machineStore)
				flags = flags | CRYPT_MACHINE_KEYSET;
			return flags;
		}
		static Exception createWin32Exception(string fcn, int win32Err)
		{
			return new Exception(string.Format("{0} failed, see inner exception for details", fcn),
				Marshal.GetExceptionForHR(win32Err));
		}
		static Exception createWin32Exception(string fcn)
		{
			return createWin32Exception(fcn, Marshal.GetHRForLastWin32Error());
		}

		const uint CRYPT_VERIFYCONTEXT = 0xF0000000;
		const uint CRYPT_NEWKEYSET = 0x00000008;
		const uint CRYPT_DELETEKEYSET = 0x00000010;
		const uint CRYPT_MACHINE_KEYSET = 0x00000020;
		const uint CRYPT_SILENT = 0x00000040;

		const uint PROV_RSA_FULL = 1;
		const uint PP_ENUMCONTAINERS = 2;
		const uint CRYPT_FIRST = 1;
		const uint CRYPT_NEXT = 2;
		const uint SHOP_FILEPATH = 2;

		const int NTE_BAD_KEYSET = unchecked((int)0x80090016L);
		const int ERROR_NO_MORE_ITEMS = 259;

		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool CryptAcquireContext(out IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, uint dwFlags);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlagsMustBeZero);

		[DllImport("advapi32.dll", CharSet = CharSet.Ansi, SetLastError = true, EntryPoint = "CryptGetProvParam", ExactSpelling = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool CryptGetProvParamAnsiString(IntPtr hProv, uint dwParam, StringBuilder sb,
		   ref uint cch, uint dwFlags);

	}
	#endregion
}